/**
 * Copyright 2008 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package kiff.entity;

import java.util.Date;
import java.util.Set;
import java.util.TreeSet;

import javax.persistence.Basic;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;
import javax.persistence.Version;

import kiff.dao.ForumDAO;
import kiff.enumerator.ForumStatus;
import kiff.enumerator.ForumType;
import kiff.security.Acl;
import kiff.security.Principal;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ReflectionToStringBuilder;

/**
 * Forum entity.
 * @author Adam
 * @version $Id: Forum.java 73 2008-12-02 06:28:25Z a.ruggles $
 */
@Entity @Table(name = "forums")
@NamedQueries({
	@NamedQuery(name = ForumDAO.FIND_BY_STATUS,
			query = "select f from Forum f where f.status in (:statuses) order by f.parent, f.order")
})
public class Forum implements CoreEntity<Integer>, UpdateAware<Forum>, Comparable<Forum>, TrackAware, Principal {
    /**
	 * Serial Version UID.
	 */
	private static final long serialVersionUID = -7584651543365718955L;

	/**
     * The Date the forum was created.
     */
    private Date created;

    /**
     * The user that created the forum.
     */
    private User creator;

	/**
     * The description of the forum.
     */
    private String description;

    /**
     * The list of sub-forums for this forum.
     */
    private Set<Forum> forums = new TreeSet<Forum>();

	/**
     * The unique identifier of the Forum object.
     */
    private Integer id;

	/**
     * The last topic that was posted in this forum.
     */
    private Topic lastTopic;

	/**
     * The Date the forum was last modified.
     */
    private Date modified;

	/**
     * The name for the forum.
     */
    private String name;

	/**
     * The order value, used to order the forum in relation to other forums.
     */
    private Integer order;

	/**
     * The parent forum, if one exists.
     */
    private Forum parent;

	/**
     * Access Control list containing roles and permissions assigned to the forum.
     */
    private Acl permissions;

	/**
     * The number of posts for this forum.
     */
    private Integer postCount;

    /**
     * The forum status.
     */
    private ForumStatus status;

    /**
     * The number of topics for this forum.
     */
    private Integer topicCount;

    /**
     * A set of topics associated with the forum. 
     */
    private Set<Topic> topics = new TreeSet<Topic>();

    /**
     * The forum type.
     */
    private ForumType type;

    /**
     * The version of the Forum object, used in the persistence layer for optimistic concurrency control.
     */
    private Integer version;

	/**
	 * @see java.lang.Comparable#compareTo(java.lang.Object)
	 */
	public int compareTo(final Forum forum) {
		if (this.order == null || forum == null || forum.getOrder() == null) {
			return -1;
		}
		return this.order.compareTo(forum.getOrder());
	}

	/**
	 * {@inheritDoc}
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(final Object obj) {
		if (this == obj) {
			return true;
		}
		if (!(obj instanceof Forum)) {
			return false;
		}
		Forum rhs = (Forum) obj;
		// If both ID's are not null compare by id.
		if (this.id != null && rhs.getId() != null) {
			return this.id.equals(rhs.getId());
		}
		return new EqualsBuilder()
			.append(name, rhs.getName())
			.append(created, rhs.getCreated())
			.isEquals();
	}

    /**
	 * Returns created.
	 * @return Returns the created.
	 */
    @Temporal(TemporalType.TIMESTAMP)
	public Date getCreated() {
		return created;
	}

    /**
	 * {@inheritDoc}
	 * @see kiff.entity.TrackAware#getCreator()
	 */
	@ManyToOne(optional = false, fetch = FetchType.LAZY) @JoinColumn(name = "creator_id")
	public User getCreator() {
		return creator;
	}

	/**
	 * Returns description.
	 * @return Returns the description.
	 */
	public String getDescription() {
		return description;
	}

	/**
	 * Returns forums.
	 * @return Returns the forums.
	 */
	@OneToMany(mappedBy = "parent")
	public Set<Forum> getForums() {
		return forums;
	}

	/**
	 * Returns id.
	 * @return Returns the id.
	 */
	@Id @GeneratedValue(strategy = GenerationType.AUTO)
	public Integer getId() {
		return id;
	}

    /**
	 * @return the lastTopic
	 */
    @ManyToOne(optional = true) @JoinColumn(name = "topic_id")
    @org.hibernate.annotations.OptimisticLock(excluded = true)
	public Topic getLastTopic() {
		return lastTopic;
	}

	/**
	 * Returns modified.
	 * @return Returns the modified.
	 */
    @Temporal(TemporalType.TIMESTAMP)
	public Date getModified() {
		return modified;
	}

	/**
	 * Returns name.
	 * @return Returns the name.
	 */
	@Column(length = 200, nullable = false)
	public String getName() {
		return name;
	}

	/**
	 * Returns order.
	 * @return Returns the order.
	 */
	@org.hibernate.annotations.Index(name = "forum_order_idx") @Column(name = "forum_order", nullable = false)
	public Integer getOrder() {
		return order;
	}

	/**
	 * Returns parent.
	 * @return Returns the parent.
	 */
	@ManyToOne(optional = true) @JoinColumn(name = "parent_id")
	public Forum getParent() {
		return parent;
	}

	/**
	 * Returns permissions.
	 * @return the permissions.
	 */
    @OneToOne(targetEntity = ForumAcl.class, cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "owner")
	public Acl getPermissions() {
		return permissions;
	}

	/**
	 * Returns postCount.
	 * @return Returns the postCount.
	 */
	@Basic(optional = false) @org.hibernate.annotations.OptimisticLock(excluded = true)
	public Integer getPostCount() {
		return postCount;
	}

	/**
	 * Returns status.
	 * @return the status.
	 */
    @org.hibernate.annotations.Type(
            type = "kiff.enumerator.GenericEnumUserType",
            parameters = {
                    @org.hibernate.annotations.Parameter(
                            name  = "enumClass",               
                            value = "kiff.enumerator.ForumStatus"),
                    @org.hibernate.annotations.Parameter(
                            name  = "identifierMethod",
                            value = "getValue"),
                    @org.hibernate.annotations.Parameter(
                            name  = "valueOfMethod",
                            value = "fromValue")
            }
    )
    @Basic(optional = false)
	public ForumStatus getStatus() {
		return status;
	}

	/**
	 * Returns topicCount.
	 * @return Returns the topicCount.
	 */
	@Basic(optional = false) @org.hibernate.annotations.OptimisticLock(excluded = true)
	public Integer getTopicCount() {
		return topicCount;
	}

	/**
	 * Returns topics.
	 * @return the topics to return.
	 */
    @Transient
	public Set<Topic> getTopics() {
		return topics;
	}

	/**
	 * Returns type.
	 * @return the type.
	 */
	public ForumType getType() {
		return type;
	}

	/**
	 * Returns version.
	 * @return Returns the version.
	 */
	@Version
	public Integer getVersion() {
		return version;
	}

	/**
	 * @see java.lang.Object#hashCode()
	 */
	public int hashCode() {
		return new HashCodeBuilder(3, 37)
			.append(name)
			.append(created)
			.toHashCode();
	}

	/**
	 * Sets created.
	 * @param created The created to set.
	 */
	public void setCreated(final Date created) {
		this.created = created;
	}

	/**
	 * {@inheritDoc}
	 * @see kiff.entity.TrackAware#setCreator(kiff.entity.User)
	 */
	public void setCreator(final User creator) {
		this.creator = creator;
	}

	/**
	 * Sets description.
	 * @param description The description to set.
	 */
	public void setDescription(final String description) {
		this.description = description;
	}

	/**
	 * Sets forums.
	 * @param forums The forums to set.
	 */
	public void setForums(final Set<Forum> forums) {
		this.forums = forums;
	}

	/**
	 * Sets id.
	 * @param id The id to set.
	 */
	public void setId(final Integer id) {
		this.id = id;
	}

	/**
	 * @param lastTopic the lastTopic to set
	 */
	public void setLastTopic(final Topic lastTopic) {
		this.lastTopic = lastTopic;
	}

    /**
	 * Sets modified.
	 * @param modified The modified to set.
	 */
	public void setModified(final Date modified) {
		this.modified = modified;
	}
    /**
	 * Sets name.
	 * @param name The name to set.
	 */
	public void setName(final String name) {
		this.name = name;
	}
    /**
	 * Sets order.
	 * @param order The order to set.
	 */
	public void setOrder(final Integer order) {
		this.order = order;
	}
    /**
	 * Sets parent.
	 * @param parent The parent to set.
	 */
	public void setParent(final Forum parent) {
		this.parent = parent;
	}
    /**
	 * Sets the permissions.
	 * @param permissions the permissions to set.
	 */
	public void setPermissions(final Acl permissions) {
		this.permissions = permissions;
	}

	/**
	 * Sets postCount.
	 * @param postCount The postCount to set.
	 */
	public void setPostCount(final Integer postCount) {
		this.postCount = postCount;
	}

	/**
	 * Sets status.
	 * @param status the status to set.
	 */
	public void setStatus(final ForumStatus status) {
		this.status = status;
	}

	/**
	 * Sets topicCount.
	 * @param topicCount The topicCount to set.
	 */
	public void setTopicCount(final Integer topicCount) {
		this.topicCount = topicCount;
	}

	/**
	 * Sets topics.
	 * @param topics the topics to set.
	 */
	public void setTopics(final Set<Topic> topics) {
		this.topics = topics;
	}

	/**
	 * Sets type.
	 * @param type the type to set.
	 */
	@org.hibernate.annotations.Type(
            type = "kiff.enumerator.GenericEnumUserType",
            parameters = {
                    @org.hibernate.annotations.Parameter(
                            name  = "enumClass",               
                            value = "kiff.enumerator.ForumType"),
                    @org.hibernate.annotations.Parameter(
                            name  = "identifierMethod",
                            value = "getValue"),
                    @org.hibernate.annotations.Parameter(
                            name  = "valueOfMethod",
                            value = "fromValue")
            }
    )
    @Basic(optional = false)
	public void setType(final ForumType type) {
		this.type = type;
	}

	/**
	 * Sets version.
	 * @param version The version to set.
	 */
	public void setVersion(final Integer version) {
		this.version = version;
	}

	/**
	 * {@inheritDoc}
	 * @see java.lang.Object#toString()
	 */
	public String toString() {
		return new ReflectionToStringBuilder(this)
			// Exclude any lazy members.
			.setExcludeFieldNames(
			new String[] {"forums", "lastTopic", "parent", "permissions", "topics", "creator"})
			.toString();
	}

	/**
	 * {@inheritDoc}
	 * @see kiff.entity.UpdateAware#update(java.lang.Object)
	 */
	public void update(final Forum entity) {
		if (entity.getCreator() != null) {
			this.creator = entity.getCreator();
		}
		if (entity.getDescription() != null) {
			this.description = entity.getDescription();
		}
		if (entity.getForums() != null) {
			this.forums = entity.getForums();
		}
		if (entity.getOrder() != null) {
			this.order = entity.getOrder();
		}
		if (entity.getParent() != null) {
			this.parent = entity.getParent();
		}
		if (entity.getPostCount() != null) {
			this.postCount = entity.getPostCount();
		}
		if (entity.getName() != null) {
			this.name = entity.getName();
		}
		if (entity.getTopicCount() != null) {
			this.topicCount = entity.getTopicCount();
		}
	}
}
